package com.llcngu.util.util;

import com.llcngu.util.base.BaseTree;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 树形结构数据处理工具类，使用该类进行数据操作时，对应的实体类需要继承BaseTree接口类
 * @author llcngu
 * @version v1.0
 * @date 2021/5/14 21:08
 * @since v1.0
 */
public class BaseTreeUtil {

    private BaseTreeUtil() {}

    /**
     * 根据输入的数据列表构造树形结构数据
     * 如果数据列表中只包含一棵树，则只返回一个树对象；如果数据列表中包含多棵树，则返回多个树对象
     * @author llcngu
     * @date 2021/5/14 21:08
     * @param treeNodeList 所有的节点列表
     * @return 组织好的树形结构数据
     */
    public static <T extends BaseTree> List<T> buildTree(List<T> treeNodeList) {
        // 1、列表校验
        if (null == treeNodeList || treeNodeList.isEmpty()) {
            return new ArrayList<>();
        }
        // 2、过滤出根节点
        List<T> rootNodeList = new ArrayList<>();
        treeNodeList.forEach(node -> {
            if (isRootNode(node)) {
                rootNodeList.add(node);
            }
        });
        // 3、如果没有根节点返回空列表
        if (rootNodeList.isEmpty()) {
            return rootNodeList;
        }
        // 4、遍历根节点，构造树形结构
        rootNodeList.forEach(rootNode -> getChildren(rootNode, treeNodeList));
        return rootNodeList;
    }

    /**
     * 判断某个节点是否是根节点，当节点层级为0或者父节点ID为null或者节点父ID为空字符串时，都认为该节点是根节点
     * @author llcngu
     * @date 2021/5/14 21:08
     * @param node 某个节点
     * @return true：是根节点；false：不是根节点；
     */
    private static <T extends BaseTree> boolean isRootNode(T node) {
        if (node != null) {
            if (node.getLevel() != null && node.getLevel() == 0) {
                return true;
            }
            return node.getParentId() == null || "".equals(node.getParentId());
        }
        return false;
    }

    /**
     * 构造某个节点的子节点列表，支持无限层级
     * @author llcngu
     * @date 2021/5/14 21:08
     * @param node 某个节点
     * @param treeNodeList 所有节点
     */
    private static <T extends BaseTree> void getChildren(T node, List<T> treeNodeList) {
        if (node != null) {
            List<T> children = new ArrayList<>();
            // 遍历所有节点，如果某个节点父ID等于当前节点的ID，则存在父子关系
            treeNodeList.forEach(item -> {
                if (node.getId() != null && node.getId().equals(item.getParentId())) {
                    children.add(item);
                    // 如果某个节点还包含子节点，则递归调用该方法进行处理
                    if (hasChildren(item, treeNodeList)) {
                        // 此时节点为非叶子节点
                        item.setLeafFlag(false);
                        getChildren(item, treeNodeList);
                    } else {
                        // 此时节点为叶子节点
                        item.setLeafFlag(true);
                        // 叶子结点的子节点属性设置为空列表
                        item.setChildren(new ArrayList<>());
                    }
                }
            });
            // 设置根节点是否是叶子节点
            node.setLeafFlag(children.isEmpty());
            node.setChildren(children);
        }
    }

    /**
     * 判断某个节点是否有子节点
     * @author llcngu
     * @date 2021/5/14 21:08
     * @param node 某个节点
     * @param treeNodeList 所有节点
     * @return true：有；false：没有
     */
    private static <T extends BaseTree> boolean hasChildren(T node, List<T> treeNodeList) {
        if (node != null && node.getId() != null && treeNodeList != null && !treeNodeList.isEmpty()) {
            for (T item : treeNodeList) {
                if (node.getId().equals(item.getParentId())) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 根据根节点对树形结构进行分组平铺，即根据根节点分组，将其所有的子节点放到一个List中，不构造树形结构
     * @author llcngu
     * @date 2021/5/14 21:08
     * @param treeNodeList 所有的节点列表
     * @return Map<String, List<T>>
     */
    public static <T extends BaseTree> Map<String, List<T>> flatByRootNode(List<T> treeNodeList) {
        Map<String, List<T>> resultMap = new HashMap<>(16);
        // 1、列表校验
        if (null == treeNodeList || treeNodeList.isEmpty()) {
            return resultMap;
        }
        // 2、过滤出根节点
        List<T> rootNodeList = new ArrayList<>();
        treeNodeList.forEach(node -> {
            if (isRootNode(node)) {
                rootNodeList.add(node);
            }
        });
        // 3、如果没有根节点返回空的Map集合
        if (rootNodeList.isEmpty()) {
            return resultMap;
        }
        rootNodeList.forEach(rootNode -> {
            // 此处的List用于在递归中存储某个根节点的所有子节点
            List<T> childrenList = new ArrayList<>();
            flatChildren(rootNode, treeNodeList, childrenList);
            resultMap.put(rootNode.getId(), childrenList);
        });
        return resultMap;
    }

    /**
     * 将根节点下的子节点进行平铺处理，即将某个根节点下的所有子节点放到同一个List中
     * @author llcngu
     * @date 2021/5/14 21:08
     * @param node 某个节点
     * @param treeNodeList 所有节点
     * @param childrenList 某个根节点的所有子节点
     */
    private static <T extends BaseTree> void flatChildren(T node, List<T> treeNodeList, List<T> childrenList) {
        if (node != null && treeNodeList != null && !treeNodeList.isEmpty() && childrenList != null) {
            // 遍历所有节点，如果某个节点父ID等于当前节点的ID，则存在父子关系
            treeNodeList.forEach(item -> {
                if (node.getId() != null && node.getId().equals(item.getParentId())) {
                    childrenList.add(item);
                    // 如果某个节点还包含子节点，则递归调用该方法进行处理
                    if (hasChildren(item, treeNodeList)) {
                        // 此时节点为非叶子节点
                        item.setLeafFlag(false);
                        flatChildren(item, treeNodeList, childrenList);
                    } else {
                        // 此时节点为叶子节点
                        item.setLeafFlag(true);
                    }
                    // 所有节点属性设置为空列表
                    item.setChildren(new ArrayList<>());
                }
            });
            // 设置根节点是否是叶子节点
            node.setLeafFlag(childrenList.isEmpty());
        }
    }
}
